
#define UNICODE
#define _UNICODE
#define __ARGPARSE_C
#define _ARGPARSEAPI __declspec(dllexport)
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "argparse.h"

_ARGPARSEAPI ArgDict CreateArgDict()
{
    ArgDict self;
    self.keys = CreateObject();
    self.values = CreateObject();
    self.contact = CreateObject();
    self.idx = 0; // index of values
    return self;
}
_ARGPARSEAPI void AD_AppendInt(ArgDict *self, TCHAR *key, BOINT i)
{
    BO_PushString(&self->keys, key);
    int arg = ((int)self->idx << 16) |1;
    BO_PushInt(&self->contact, arg);
    BO_PushInt(&self->values, i);
    self->idx ++;
}
_ARGPARSEAPI void AD_AppendFloat(ArgDict *self, TCHAR *key, BOFLOAT f)
{
    BO_PushString(&self->keys, key);
    int arg = ((int)self->idx << 16) |1;
    BO_PushInt(&self->contact, arg);
    BO_PushFloat(&self->values, f);
    self->idx ++;
}
_ARGPARSEAPI void AD_AppendString(ArgDict *self, TCHAR *key, TCHAR *s)
{
    BO_PushString(&self->keys, key);
    int arg = ((int)self->idx << 16) |1;
    BO_PushInt(&self->contact, arg);
    BO_PushString(&self->values, s);
    self->idx ++;
}
_ARGPARSEAPI void AD_AppendIntList(ArgDict *self, TCHAR *key, BOINT *lo, int16_t len)
{
    BO_PushString(&self->keys, key);
    int arg = ((int)self->idx << 16) | ((int)len);
    BO_PushInt(&self->contact, arg);
    for(int j = 0; j < len; j++) {
        BO_PushInt(&self->values, lo[j]);
    }
    self->idx += len;
}
_ARGPARSEAPI void AD_AppendFloatList(ArgDict *self, TCHAR *key, BOFLOAT *lo, int16_t len)
{
    BO_PushString(&self->keys, key);
    int arg = ((int)self->idx << 16) | ((int)len);
    BO_PushInt(&self->contact, arg);
    for(int j = 0; j < len; j++) {
        BO_PushFloat(&self->values, lo[j]);
    }
    self->idx += len;
}
_ARGPARSEAPI void AD_AppendStringList(ArgDict *self, TCHAR *key, TCHAR *s[], int16_t len)
{
    BO_PushString(&self->keys, key);
    int arg = ((int)self->idx << 16) | ((int)len);
    BO_PushInt(&self->contact, arg);
    for(int j = 0; j < len; j++) {
        BO_PushString(&self->values, s[j]);
    }
    self->idx += len;
}

_ARGPARSEAPI bo_datum *AD_SeekKey(ArgDict *self, TCHAR *key)
{
    bo_datum obj;
    obj.type = BT_STR;
    obj.s = key;
    int i = BO_SeekDatum(&self->keys, &obj);
    if(i >= 0) {
        return BO_GetDatum(&self->values, (BO_GetDatum(&self->contact, i)->i) >> 16);
    } else {
        return NULL;
    }
}
bo_datum *__AD_SeekKey(ArgDict *self, TCHAR *key, int *len, int *index)
{
    bo_datum obj;
    obj.type = BT_STR;
    obj.s = key;
    int i = BO_SeekDatum(&self->keys, &obj);
    if(i >= 0) {
        int k = BO_GetDatum(&self->contact, i)->i;
        *len = ((int)(k << 16)) >> 16;
        *index = k >> 16;
        return BO_GetDatum(&self->values, *index);
    } else {
        return NULL;
    }
}
_ARGPARSEAPI bo_datum *AD_SeekNextValue(ArgDict *self, TCHAR *key, bo_datum *prev)
{
    int len = 1;
    int i = 0;
    bo_datum *tree = __AD_SeekKey(self, key, &len, &i);
    if(tree == NULL){
        return NULL;
    }
    bo_datum *ptr = tree;
    int p = 1;
    if(prev != NULL){
        while(ptr != prev) {
            i++;
            p++;
            if(p > len) {
                return NULL;
            }
            ptr = BO_GetDatum(&self->values, i);
        }
        i++;
        p++;
        if(p > len) {
            return NULL;
        }
        ptr = BO_GetDatum(&self->values, i);
        return ptr;
    } else {
        return tree;
    }
}
_ARGPARSEAPI bo_datum AD_GetValue(ArgDict *self, TCHAR *key, bo_datum *_Default)
{
    bo_datum *p = AD_SeekKey(self, key);
    if (p == NULL) {
        return *_Default;
    } else {
        return *p;
    }
}
_ARGPARSEAPI bo_datum AD_GetNextValue(ArgDict *self, TCHAR *key, bo_datum *prev, bo_datum *_Default)
{
    bo_datum *p = AD_SeekNextValue(self, key, prev);
    if (p == NULL) {
        return *_Default;
    } else {
        return *p;
    }
}

/*
_ARGPARSEAPI errno_t AD_ReadString(ArgDict *self, TCHAR *str)
{
    // 0 k 1 : 2 v 3 ,;
    int place = 0;
    TCHAR buf[1000];
    TCHAR *p = str;
    while(true) {
        switch (place)
        {
        case 0: {
            
        } break;
        
        case 1: {
            
        } break;
        
        case 2: {
            
        } break;
        
        case 3: {
            
        } break;
        
        default:
            break;
        }
    }
}
_ARGPARSEAPI errno_t AD_ReadText(ArgDict *self, TCHAR *filename)
{
    
}
*/

_ARGPARSEAPI errno_t AD_ParseCmdLn(ArgDict *self, const TCHAR *cmd, TCHAR sep)
{
    TCHAR buf[520];
    int ic = 0;
    int ib = 0;
    int suc = 0;
    while(true){
        ib = 0;
        while(cmd[ic] != 0 && cmd[ic]!= sep && cmd[ic] != (TCHAR)(' ')) {
            buf[ib] = cmd[ic];
            ib++;
            ic++;
            if(ib >= 260){
                break;
            }
        }
        buf[ib] = 0;
        if(cmd[ic] == 0) {
            break;
        }
        if(cmd[ic] == (TCHAR)(' ')) {
            ic++;
            continue;
        }
        ic++; // skip sep
        if (ib == 0) {
            while(cmd[ic] != (TCHAR)(' ') && cmd[ic] != 0) {
                ic++;
            }
            continue;
        }
        ib = 261;
        while(cmd[ic] != 0 && cmd[ic] != (TCHAR)(' ')) {
            buf[ib] = cmd[ic];
            ib++;
            ic++;
            if(ib >= 519){
                break;
            }
        }
        buf[ib] = 0;
        if (ib == 261) {
            ;
        }
        if (isInteger(&buf[261])) {
            int d = 0;
            _stscanf(&buf[261], _T("%d"), &d);
            AD_AppendInt(self, buf, d);
        }
        else if (isDecimal(&buf[261])) {
            double f = 0;
            _stscanf(&buf[261], _T("%lf"), &f);
            AD_AppendFloat(self, buf, f);
        }
        else {
            AD_AppendString(self, buf, &buf[261]);
        }
        suc++;
        if (cmd[ic] == 0) {
            break;
        }
        //wprintf(L"parse %d. ", suc);
    }
    return suc;
}


_ARGPARSEAPI errno_t AD_FreeArgDict(ArgDict *self)
{
    BO_FreeObject(&self->keys);
    BO_FreeObject(&self->values);
    BO_FreeObject(&self->contact);
    return 0;
}
_ARGPARSEAPI TCHAR *AD_ConvertToReadableFormat(ArgDict *self, TCHAR *to)
{
    TCHAR buf[260];
    _stprintf(to, _T("{"));
    int idk = 0;
    bo_datum *p;
    bo_datum *q;
    bo_datum *r;
    while (true) {
        p = BO_GetDatum(&self->keys, idk);
        if (p == NULL) {
            break;
        }
        if (p->type != BT_STR) {
            _tcscat(to, _T("STR?????"));
            return NULL;
        }
        _stprintf(buf, _T("%s : "), p->s);
        _tcscat(to, buf);
        q = AD_SeekKey(self, p->s);
        r = AD_SeekNextValue(self, p->s, q);
        if (r == NULL) {
            switch (q->type)
            {
            case BT_INT: {
                _stprintf(buf, _T("%d, "), q->i);
                _tcscat(to, buf);
            } break;
            case BT_FLOAT: {
                _stprintf(buf, _T("%lf, "), q->f);
                _tcscat(to, buf);
            } break;
            case BT_STR: {
                _stprintf(buf, _T("%s, "), q->s);
                _tcscat(to, buf);
            } break;
            
            default:
                _stprintf(buf, _T("<unknown>%d, "), (int)q->type);
                _tcscat(to, buf);
                break;
            }
            //wprintf(L"seek one, ");
        }
        else {
            _stprintf(buf, _T("["));
            _tcscat(to, buf);
            while(true){
                switch (q->type)
                {
                case BT_INT: {
                    _stprintf(buf, _T("%d, "), q->i);
                    _tcscat(to, buf);
                } break;
                case BT_FLOAT: {
                    _stprintf(buf, _T("%lf, "), q->f);
                    _tcscat(to, buf);
                } break;
                case BT_STR: {
                    _stprintf(buf, _T("%s, "), q->s);
                    _tcscat(to, buf);
                } break;
                
                default:
                    _stprintf(buf, _T("<unknown>%d, "), (int)q->type);
                    _tcscat(to, buf);
                    break;
                }
                q = r;
                r = AD_SeekNextValue(self, p->s, q);
                if (r == NULL) {
                    break;
                }
            }
            switch (q->type)
            {
            case BT_INT: {
                _stprintf(buf, _T("%d], "), q->i);
                _tcscat(to, buf);
            } break;
            case BT_FLOAT: {
                _stprintf(buf, _T("%lf], "), q->f);
                _tcscat(to, buf);
            } break;
            case BT_STR: {
                _stprintf(buf, _T("%s], "), q->s);
                _tcscat(to, buf);
            } break;
            
            default:
                _stprintf(buf, _T("<unknown>%d], "), (int)q->type);
                _tcscat(to, buf);
                break;
            }
            //wprintf(L"seek lot, ");
        }
        idk++;
        //wprintf(L"write %d.", idk);
    }
    _tcscat(to, _T("}"));
    return to;
}

_Bool _ARGPARSEAPI isInteger(const TCHAR* itstr)
{
    int index = 0;
    if (itstr[index] == (TCHAR)('-')  or  itstr[index] == (TCHAR)('+')) {
        index ++;
    }
    while (itstr[index] != 0) {
        if (!_istdigit(itstr[index])) {
            return false;
        }
        index ++;
    }
	return true;
}

_Bool _ARGPARSEAPI isDecimal(TCHAR *s)
{
    TCHAR s1[30];
    TCHAR s2[30];
    int index = 0;
    int i = 0;
    while (s[index] != (TCHAR)('.') && s[index] != 0) {
        s1[i] = s[index];
        index++;
        i++;
        if (i >= 30) {
            return false;
        }
    }
    if (index == 0) {
        return false;
    }
    s1[i] = 0;
    index++;
    i = 0;
    while (s[index] != 0) {
        s2[i] = s[index];
        i++;
        index++;
        if (i >= 30) {
            return false;
        }
    }
    s2[i] = 0;
    if (isInteger(s1) && isInteger(s2)) {
        return 1;
    } else {return 0;}
}